package com.example.blockchain.thread;

import com.example.blockchain.entity.TradeObject;
import com.example.blockchain.entity.JSONObject;
import com.example.blockchain.entity.block.*;
import com.example.blockchain.entity.block.Dictionary;
import com.example.blockchain.service.impl.BlockServiceImpl;
import com.example.blockchain.service.impl.DicServiceImpl;
import com.example.blockchain.service.impl.PendingServiceImpl;
import com.example.blockchain.timer.UpdateTimer;
import com.example.blockchain.utils.DataUtils;
import com.example.blockchain.utils.DateUtils;
import com.example.blockchain.utils.InitUtils;
import com.example.blockchain.utils.encrypt.EncryptUtil;
import com.example.blockchain.utils.http.HttpHelper;
import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.*;

@Component
public class WorkThread extends Thread {


    @Resource
    private Mining mining;

    @Resource
    private MapFriends map;

    public WorkThread(Mining mining, MapFriends map) {
        super();
        this.mining = mining;
        this.map = map;
    }

    @Override
    public synchronized void run() {
        super.run();

        while (true) {
            try {
                String time = DateUtils.getTime();
                if (mining.isWork) {
                    System.out.println("开始运算---------------------");
                    int trueCount = 0;
                    int falseCount = 0;
                    String preBlockIndex = String.valueOf(UpdateTimer.currentMaxBlockIndex);
                    BlockServiceImpl.checkBlockTable(preBlockIndex);
                    List<Block> blocks = BlockServiceImpl.queryBlockByBlockIndex(preBlockIndex);

                    Block currentBlock = null;
                    if (blocks.size() > 0) {
                        currentBlock = blocks.get(0);
                    }
                    if (currentBlock == null) {
                        currentBlock = new Block();
                        currentBlock.setBlockHash("First block hash");
                    }
                    Dictionary diffWorkload = InitUtils.intiDifficulty();// 字典表的工作量配置
                    String maxBlockIndex = currentBlock.getBlockIndex();
                    String nextBlockIndex = getNextBlockIndex(maxBlockIndex);
                    Block block = new Block();
                    Random r = new Random();
                    String rand = String.valueOf(r.nextInt(1000000));
                    block.setBlockIndex(nextBlockIndex);
                    block.setCreateTime(time);
                    block.setWorkLoad(diffWorkload.getValue());
                    block.setCreateTime(DateUtils.getTime());
                    block.setPreBlockHash(currentBlock.getBlockHash());
                    block.setRandomNumber(rand);
                    Date runDate = new Date();
                    String blockPath = DataUtils.getBlockPath(nextBlockIndex, runDate);// gen block file path
                    block.setPath(blockPath);

                    //取出交易
                    List<String> tradeNos = new ArrayList<>();
                    List<Pending> list = PendingServiceImpl.queryPendings();
                    if (list.size() != 0) {
                        for (int i = 0; i < list.size(); i++) {
                            tradeNos.add(list.get(i).getOrderNo());
                        }
                    }
                    block.setDataJson(list.toString());

                    String outHash = EncryptUtil.encryptSHA256(block.workString());
                    System.out.println(outHash);

                    if (outHash.startsWith(diffWorkload.getValue())) {
                        System.out.println("挖到---------------------");
                        if (!mining.isWork) {
                            return;
                        }
                        block.setBlockHash(outHash);
                        for (String port : map.getFs().keySet()) {
                            Friends f = map.getFs().get(port);
                            String ip = f.getIp();
                            try {
                                String resp = HttpHelper.checkBlock(ip, block);
                                JSONObject response = new Gson().fromJson(resp, JSONObject.class);
                                if (response.getCode().equals("1")) {
                                    Boolean isTrue = (Boolean) response.getO();
                                    if (isTrue) {
                                        trueCount = trueCount + 1;
                                    } else {
                                        falseCount = falseCount + 1;
                                    }
                                }
                            } catch (Exception e) {
                                System.out.println(ip + "失败");
                                map.getFs().get(port).setFriendliness(0);
                            }
                        }

                        int count = trueCount + falseCount;
                        boolean isTrueCountMajority = false;
                        if (count > 0) {
                            isTrueCountMajority = trueCount > (count / 2);
                        }

                        if (isTrueCountMajority) {

                            TradeBodyPool tbp = new TradeBodyPool();
                            Map<String, TradeObject> tbMap = new HashMap<>();

                            for (Pending p : list) {
                                TradeObject body = new Gson().fromJson(p.getTradeBody(), TradeObject.class);
                                tbMap.put(p.getOrderNo(), body);
                            }

                            List<Block> bs = BlockServiceImpl.queryBlockByBlockIndex(block.getBlockIndex());
                            if (bs.size() > 0) {
                                deletePending(tbp);//删除pending
                                continue;
                            }
                            tbp.setTbMap(tbMap);

                            BlockFile blockFile = new BlockFile();
                            blockFile.setTbMap(tbMap);
                            blockFile.setBlockHash(block.getBlockIndex());
                            blockFile.setBlockHash(block.getBlockHash());
                            blockFile.setPreBlockHash(block.getPreBlockHash());
                            String blockString = new Gson().toJson(blockFile);//区块文件信息

                            BlockDownLoad bdl = new BlockDownLoad();
                            bdl.setBlock(block);
                            bdl.setBlockFileStr(blockString);
                            bdl.setMaxBlockIndex(block.getBlockIndex());


                            BlockServiceImpl.checkBlockTable(block.getBlockIndex());//检查表是否存在

                            BlockServiceImpl.save(block);//保存区块信息
                            BlockServiceImpl.saveBlockFile(bdl);//保存区块文件信息
                            DicServiceImpl.updateDicBlockIndex(block.getBlockIndex());//更新当前更新到的块号
                            DicServiceImpl.updateDicMainBockIndex(bdl.getMaxBlockIndex());//更新当前更新到的块号
                            UpdateTimer.currentBlockIndex = new BigInteger(block.getBlockIndex());
                            UpdateTimer.currentMaxBlockIndex = new BigInteger(block.getBlockIndex());
                            deletePending(tbp);//删除pending

                            Thread.sleep(2000);
                            mining.isWork = true;
                        }
                    }
                }
            } catch (Exception e) {
                e.getMessage();
                mining.isWork = true;
            }


        }
    }

    public void deletePending(TradeBodyPool tbp) {
        List<String> tradeNos = new ArrayList<String>();
        Map<String, TradeObject> tbMap = tbp.getTbMap();
        for (Map.Entry<String, TradeObject> entry : tbMap.entrySet()) {
            tradeNos.add(entry.getValue().getHashNo());
        }
        PendingServiceImpl.deletePendings(tradeNos);//删除已经入块验证过的信息
    }

    private String getNextBlockIndex(String blockIndex) {
        BigInteger b = BigInteger.ONE;
        if (StringUtils.isNotBlank(blockIndex)) {
            b = new BigInteger(blockIndex);
            b = b.add(new BigInteger("1"));// index ++
        }
        return b.toString();
    }


}

